package org.mika.asm;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

import net.sf.cglib.asm.ClassWriter;
import net.sf.cglib.asm.Type;
import net.sf.cglib.core.ClassEmitter;
import net.sf.cglib.core.ClassGenerator;
import net.sf.cglib.core.CodeEmitter;
import net.sf.cglib.core.Constants;
import net.sf.cglib.core.EmitUtils;
import net.sf.cglib.core.GeneratorStrategy;
import net.sf.cglib.core.ReflectUtils;

public class StaticFieldsImplGenerator<T,I> implements GeneratorStrategy {
	private Class<? extends T> opener;
	private Class<? extends I> interf;	

	public StaticFieldsImplGenerator(Class<? extends T> o, Class<I> i) {
		opener = o; 
		interf = i; 
	}

	@Override
	public byte[] generate(ClassGenerator cg) throws Exception {
		ClassWriter  writer  = new ClassWriter(true);
		ClassEmitter ce = new ClassEmitter(writer);
						
		ASMUtils.generateImplementatorHeader(ce, opener, interf);
		EmitUtils.null_constructor(ce);
				
		BeanInfo info = Introspector.getBeanInfo(interf);
		for(PropertyDescriptor pd : info.getPropertyDescriptors()) {
			Method m = pd.getReadMethod();
			if(m != null) {
				// generating read method
				CodeEmitter method = EmitUtils.begin_method(ce, ReflectUtils.getMethodInfo(m), Constants.ACC_PUBLIC);
											
				method.push( pd.getName() );
				
				method.invoke_static(
					Type.getType( opener ),
					ReflectUtils.getSignature(
						opener.getMethod( StaticFieldsOpenerGenerator.READ , new Class[]{
							String.class 
						})
					)
				);
				
				ASMUtils.returnFromStack(m.getReturnType(), method);
			}
			m = pd.getWriteMethod();
			if(m != null) {
				// generating write method
				CodeEmitter method = EmitUtils.begin_method(ce, ReflectUtils.getMethodInfo(m), Constants.ACC_PUBLIC);
								
				method.push( pd.getName() );
				ASMUtils.copyArg(method, pd.getPropertyType());
				
				method.invoke_static(
					Type.getType( opener ),
					ReflectUtils.getSignature(
						opener.getMethod( StaticFieldsOpenerGenerator.WRITE , new Class[]{
							String.class, Object.class 
						})
					)
				);
				
				ASMUtils.returnFromStack(m.getReturnType(), method);
			}
		}
		
		ce.end_class();		
		return writer.toByteArray();
	}
}

